package org.openhds.extensions;

import org.openhds.domain.util.CalendarAdapter;
import com.sun.codemodel.JAnnotationUse;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JDocComment;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JVar;

/**
 * A class that's responsible for building the core Visit entity table
 * using the CodeModel.
 * 
 * @author Brian
 *
 */
public class VisitTemplateBuilder implements ExtensionTemplate {
	
	JCodeModel jCodeModel;
	boolean visitTemplateBuilt = false;
	
	VisitTemplateBuilder(JCodeModel jCodeModel) {
		this.jCodeModel = jCodeModel;
	}
	
	public void buildTemplate(JDefinedClass jc) {

		JDocComment jDocComment = jc.javadoc();
		jDocComment.add("Generated by JCodeModel");
		
		jc._extends(org.openhds.domain.model.AuditableCollectedEntity.class);
		jc._implements(java.io.Serializable.class);
		
		buildClassAnnotations(jc);
		buildFieldsAndMethods(jc);
		
		visitTemplateBuilt = true;
	}
	
	public void buildClassAnnotations(JDefinedClass jc) {
		
		// create Description annotation
		JAnnotationUse jad = jc.annotate(org.openhds.domain.annotations.Description.class);
		jad.param("description", "A Visit represents a Field Worker's observation " +
		"of a specific Location within the study area at a particular date. It " +
		"can be identified by a uniquely generated identifier which the system " +
		"uses internally.");
				
		// create Entity annotation
		jc.annotate(javax.persistence.Entity.class);
		
		JAnnotationUse jat = jc.annotate(javax.persistence.Table.class);
		jat.param("name", "visit");
	}

	public void buildFieldsAndMethods(JDefinedClass jc) {

		// serial uuid
		JFieldVar jfSerial = jc.field(JMod.PUBLIC | JMod.STATIC | JMod.FINAL, long.class, "serialVersionUID");
		jfSerial.init(JExpr.lit(-211408757055967973L));
		
		// extId
		JFieldVar jfExtId = jc.field(JMod.PRIVATE , java.lang.String.class, "extId");
		jfExtId.annotate(org.openhds.domain.constraint.Searchable.class);
		JAnnotationUse jaExtIdDesc = jfExtId.annotate(org.openhds.domain.annotations.Description.class);
		jaExtIdDesc.param("description", "External Id of the visit. This id is used internally.");
		
		// getter
		JMethod jmgExtId = jc.method(JMod.PUBLIC, java.lang.String.class, "getExtId");
		JBlock jmgExtIdBlock = jmgExtId.body();
		jmgExtIdBlock._return(jfExtId);
		
		// setter
		JMethod jmsExtId = jc.method(JMod.PUBLIC, void.class, "setExtId");
		JVar jvarExtId = jmsExtId.param(java.lang.String.class, "id");
		JBlock jmsExtIdBlock = jmsExtId.body();
		jmsExtIdBlock.assign(jfExtId, jvarExtId);
			
		// visitLocation
		JFieldVar jfLocation = jc.field(JMod.PRIVATE , org.openhds.domain.model.Location.class, "visitLocation");
		jfLocation.annotate(org.openhds.domain.constraint.Searchable.class);
		jfLocation.annotate(javax.persistence.ManyToOne.class);
		JAnnotationUse jaLocationDesc = jfLocation.annotate(org.openhds.domain.annotations.Description.class);
		jaLocationDesc.param("description", "Location that this visit is for.");
		
		// getter
		JMethod jmgLocation = jc.method(JMod.PUBLIC, org.openhds.domain.model.Location.class, "getVisitLocation");
		JBlock jmgLocationBlock = jmgLocation.body();
		jmgLocationBlock._return(jfLocation);
		
		// setter
		JMethod jmsLocation = jc.method(JMod.PUBLIC, void.class, "setVisitLocation");
		JVar jvarLocation = jmsLocation.param(org.openhds.domain.model.Location.class, "location");
		JBlock jmsLocationBlock = jmsLocation.body();
		jmsLocationBlock.assign(jfLocation, jvarLocation);
		
		// visitDate
		JFieldVar jfVisitDate = jc.field(JMod.PRIVATE , java.util.Calendar.class, "visitDate");
		JAnnotationUse jaVisitDate = jfVisitDate.annotate(javax.validation.constraints.NotNull.class);
		jaVisitDate.param("message", "You must provide a visit date");
		JAnnotationUse jaPast = jfVisitDate.annotate(javax.validation.constraints.Past.class);
		jaPast.param("message", "Visit date should be in the past");
		JAnnotationUse jaTemporal = jfVisitDate.annotate(javax.persistence.Temporal.class);
		jaTemporal.param("value", javax.persistence.TemporalType.DATE);
		JAnnotationUse jaVisitDateDesc = jfVisitDate.annotate(org.openhds.domain.annotations.Description.class);
		jaVisitDateDesc.param("description", "Date of the visit.");
		
		// getter
		JMethod jmgVisitDate = jc.method(JMod.PUBLIC, java.util.Calendar.class, "getVisitDate");
		JAnnotationUse jaXmlVisitDate = jmgVisitDate.annotate(javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.class);
		jaXmlVisitDate.param("value", CalendarAdapter.class);
		JBlock jmgVisitDateBlock = jmgVisitDate.body();
		jmgVisitDateBlock._return(jfVisitDate);
		
		// setter
		JMethod jmsVisitDate = jc.method(JMod.PUBLIC, void.class, "setVisitDate");
		JVar jvarVisitDate = jmsVisitDate.param(java.util.Calendar.class, "date");
		JBlock jmsVisitDateBlock = jmsVisitDate.body();
		jmsVisitDateBlock.assign(jfVisitDate, jvarVisitDate);
		
		// roundNumber
		JFieldVar jfRoundNumber = jc.field(JMod.PRIVATE , java.lang.Integer.class, "roundNumber");
		JAnnotationUse jaCheckInteger = jfRoundNumber.annotate(org.openhds.domain.constraint.CheckInteger.class);
		jaCheckInteger.param("min", 1);
		JAnnotationUse jaDobAspectDesc = jfRoundNumber.annotate(org.openhds.domain.annotations.Description.class);
		jaDobAspectDesc.param("description", "Round number for the visit.");
		
		// getter
		JMethod jmgRoundNumber = jc.method(JMod.PUBLIC, java.lang.Integer.class, "getRoundNumber");
		JBlock jmgRoundNumberBlock = jmgRoundNumber.body();
		jmgRoundNumberBlock._return(jfRoundNumber);
		
		// setter
		JMethod jmsRoundNumber = jc.method(JMod.PUBLIC, void.class, "setRoundNumber");
		JVar jvarRoundNumber = jmsRoundNumber.param(java.lang.Integer.class, "roundNum");
		JBlock jmsRoundNumberBlock = jmsRoundNumber.body();
		jmsRoundNumberBlock.assign(jfRoundNumber, jvarRoundNumber);
		
		// extensions
		JClass basicListExtensions = jCodeModel.ref(java.util.List.class);
		basicListExtensions = basicListExtensions.narrow(org.openhds.domain.model.Extension.class);
		JFieldVar jfExtensions = jc.field(JMod.PRIVATE , basicListExtensions, "extensions");
		JClass jExtensionsClassRef = jCodeModel.ref(java.util.ArrayList.class);
		jExtensionsClassRef = jExtensionsClassRef.narrow(org.openhds.domain.model.Extension.class);
		jfExtensions.init(JExpr._new(jExtensionsClassRef));
		JAnnotationUse jaExtensionsTarget = jfExtensions.annotate(javax.persistence.OneToMany.class);
		jaExtensionsTarget.param("mappedBy", "entity");
		jaExtensionsTarget.param("cascade",  javax.persistence.CascadeType.ALL);
		jaExtensionsTarget.param("fetch", javax.persistence.FetchType.EAGER);
		JAnnotationUse jaResidenciesDesc = jfExtensions.annotate(org.openhds.domain.annotations.Description.class);
		jaResidenciesDesc.param("description", "The assigned extension and their values specific for this entity.");
		
		// getter
		JMethod jmgExtensions = jc.method(JMod.PUBLIC, basicListExtensions, "getExtensions");
		JBlock jmgExtensionsBlock = jmgExtensions.body();
		jmgExtensionsBlock._return(jfExtensions);
		
		// setter
		JMethod jmsExtensions = jc.method(JMod.PUBLIC, void.class, "setExtensions");
		JVar jvarExtensions = jmsExtensions.param(basicListExtensions, "list");
		JBlock jmsExtensionsBlock = jmsExtensions.body();
		jmsExtensionsBlock.assign(jfExtensions, jvarExtensions);
	}
}
